package br.ufmg.vod.wbr.graphs;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import br.ufmg.vod.wbr.IOUtil;

public class IDXGraphLoader implements GraphLoader<FastDirectedGraph> {

	protected static final int INT_SIZE = 4;
	protected static final int IDX_REGISTER_SIZE = 2*INT_SIZE;
	protected static final int WBR_NUM_DOCS = 5939061;
	
	private final String inLinkIdxFilename;
	private final String inLinkDataFilename;
	private final String outLinkIdxFilename;
	private final String outLinkDataFilename;
	private final boolean sample;
	private final int sampleSize;

	public IDXGraphLoader(String inLinkIdxFilename, String inLinkDataFilename, String outLinkIdxFilename, String outLinkDataFilename, boolean sample, int sampleSize) throws IOException{
		this.inLinkIdxFilename = inLinkIdxFilename;
		this.inLinkDataFilename = inLinkDataFilename;
		this.outLinkIdxFilename = outLinkIdxFilename;
		this.outLinkDataFilename = outLinkDataFilename;
		this.sample = sample;
		this.sampleSize = sampleSize;
	}
	
	public FastDirectedGraph loadGraph() throws Exception {
		BufferedInputStream inLinkIdx = null;
		BufferedInputStream inLinkData = null;
		BufferedInputStream outLinkIdx = null;
		BufferedInputStream outLinkData = null;
		
		try {
			//Open data holders
			File inLinkIdxFile = new File(inLinkIdxFilename);
			File outLinkIdxFile = new File(outLinkIdxFilename);
			inLinkIdx = new BufferedInputStream(new FileInputStream(inLinkIdxFile));
			inLinkData = new BufferedInputStream(new FileInputStream(inLinkDataFilename));
			outLinkIdx = new BufferedInputStream(new FileInputStream(outLinkIdxFile));
			outLinkData = new BufferedInputStream(new FileInputStream(outLinkDataFilename));
			
			//assert everything is as expected
			assert inLinkIdxFile.length() == outLinkIdxFile.length();
			assert inLinkIdxFile.length() > 0;
			assert inLinkIdxFile.length()/IDX_REGISTER_SIZE < Integer.MAX_VALUE;
			assert inLinkIdxFile.length()/IDX_REGISTER_SIZE == WBR_NUM_DOCS;
	
			//do the hard part
			int[][][] loadData = this.loadData(inLinkIdxFile, inLinkData, inLinkIdx, outLinkIdxFile, outLinkData, outLinkIdx);
			
			return new FastDirectedGraph(loadData[0], loadData[1]);
		} finally {
			//clean yourself up!
			if (inLinkIdx != null) inLinkIdx.close();
			if (inLinkData != null) inLinkData.close();
			if (outLinkIdx != null) outLinkIdx.close();
			if (outLinkData != null) outLinkData.close();
		}
	}


	private int[][][] loadData(File inLinkIdxFile, BufferedInputStream inLinkData, BufferedInputStream inLinkIdx, File outLinkIdxFile, BufferedInputStream outLinkData, BufferedInputStream outLinkIdx) throws Exception {
		int[][][] returnValue = new int[2][][];
		
		int numVertex = (int) (this.sample ? this.sampleSize : inLinkIdxFile.length()/(IDX_REGISTER_SIZE));
		int bufferSize = (int) (this.sample ? (numVertex+1)*IDX_REGISTER_SIZE : inLinkIdxFile.length());
		
		int[][] inEdges = new int[numVertex][];
		int[][] outEdges = new int[numVertex][];
		returnValue[0] = inEdges;
		returnValue[1] = outEdges;
		
		byte[] inLinkIdxbuffer = new byte[bufferSize];
		byte[] outLinkIdxbuffer = new byte[bufferSize];
		IOUtil.fillBuffer(inLinkIdx, inLinkIdxbuffer);
		IOUtil.fillBuffer(outLinkIdx, outLinkIdxbuffer);
		
		System.out.println(inLinkIdxFile.length()+" "+inLinkIdxbuffer.length);
		long maxInDegree = 0l;
		long maxOutDegree = 0l;
		int edgeCount = 0;

		System.out.println("Will read "+ numVertex +" lists...");		
		for (int i = 0; i < numVertex; i++) {
			
			if( i%(1024*1024) == 0)	System.out.println(i/(1024*1024)+" giga lists...");

			//inEdges
			int startOffset = IOUtil.readSignedInt(inLinkIdxbuffer, i*IDX_REGISTER_SIZE);
			int endOffset = IOUtil.readSignedInt(inLinkIdxbuffer, (i+1)*IDX_REGISTER_SIZE);
			assert startOffset <= endOffset;
			byte[] buffer = new byte[(int) (endOffset-startOffset)];
			IOUtil.fillBuffer(inLinkData, buffer);
			inEdges[i] = IOUtil.toSignedInts(buffer);
			maxInDegree = inEdges[i].length > maxInDegree ? inEdges[i].length : maxInDegree;
			
	
			//outEdges
			startOffset = IOUtil.readSignedInt(outLinkIdxbuffer, i*IDX_REGISTER_SIZE);
			endOffset = IOUtil.readSignedInt(outLinkIdxbuffer, (i+1)*IDX_REGISTER_SIZE);
			buffer = new byte[(int) (endOffset-startOffset)];
			IOUtil.fillBuffer(outLinkData, buffer);
			outEdges[i]=IOUtil.toSignedInts(buffer);
			maxOutDegree = outEdges[i].length > maxOutDegree ? outEdges[i].length : maxOutDegree;
			
			edgeCount+=outEdges[i].length;
		}
		
		System.out.println("maxInDegree="+maxInDegree);
		System.out.println("maxOutDegree="+maxOutDegree);
		return returnValue;
	}
}